import uuid from 'node-uuid';
import crypto from 'crypto';
import fs from "fs";
import path from 'path';
import cp from 'child_process';
import { fileURLToPath } from "node:url";
import shelljs from 'shelljs';

import { run } from './tools/run.js';

const fp = fs.promises;
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);


export function randomChars(len = 8) {
    const uid = uuid.v4();
    const md5 = crypto.createHash('md5');
    const a = md5.update(uid).digest('hex');

    return a.slice(0, len);
}

// 使用g++ -S 命令生成汇编代码, 然后使用汇编代码的sha1值作为id
// 目的时去除代码中的注释和空格，以生成唯一id
// TODO 对于相同功能的函数，不同的函数名依旧会生成不同的汇编代码，导致id不同
export async function genIdFromCppCode(code) {

    return new Promise(async (resolve, reject) => {
        const baseName = randomChars();

        let assembly = null;
        let gpp_ok = false;

        // 如果g++运行错误，就使用gcc再试
        try {
            await fp.writeFile(path.join(__dirname, `./temp/${baseName}.cpp`), code);
            const output = await run('g++', `-S -w ${path.join(__dirname, `./temp/${baseName}.cpp -o`) + ' ' + path.join(__dirname, `./temp/${baseName}.s`)}`)
            gpp_ok = !output.stderr;
        } catch (e) {
            console.error('[genIdFromCppCode g++ Error]', e);
        }

        if (!gpp_ok) {
            try {
                await fp.writeFile(path.join(__dirname, `./temp/${baseName}.c`), code);
                const output = await run('gcc', `-S -w ${path.join(__dirname, `./temp/${baseName}.c -o`) + ' ' + path.join(__dirname, `./temp/${baseName}.s`)}`)
                
                if (output.stderr) {
                    shelljs.rm('-rf', path.join(__dirname, `./temp/${baseName}.*`));
                    resolve('');
                    return;
                } 
            } catch (e) {
                console.error('[genIdFromCppCode gcc Error]', e);
                shelljs.rm('-rf', path.join(__dirname, `./temp/${baseName}.*`));
                resolve('');
                return;
            }
        }

        assembly = await fp.readFile(path.join(__dirname, `./temp/${baseName}.s`), 'utf-8');
        // 删除第一行关于文件标题的内容
        const lines = assembly.split('\n');
        lines.splice(0, 1);
        const source = lines.join('\n');

        shelljs.rm('-rf', path.join(__dirname, `./temp/${baseName}.*`));

        resolve(crypto.createHash('sha1').update(source).digest('hex'));

        // const cmd = `g++ -S ` + path.join(__dirname, `./temp/${baseName}.cpp -o`) + ' ' + path.join(__dirname, `./temp/${baseName}.s`);
        // try {
        //     // 如果代码有错误，则不会生成汇编代码
        //     cp.exec(cmd, async (err) => {
        //         if (err) {
        //             resolve('');
        //             shelljs.rm('-rf', path.join(__dirname, `./temp/${baseName}.*`));
        //             return;
        //         }

        //         const assembly = await fp.readFile(path.join(__dirname, `./temp/${baseName}.s`), 'utf-8');
                
        //         // 删除第一行关于文件标题的内容
        //         const lines = assembly.split('\n');
        //         lines.splice(0, 1);
        //         const source = lines.join('\n');

        //         shelljs.rm('-rf', path.join(__dirname, `./temp/${baseName}.*`));

        //         resolve(crypto.createHash('sha1').update(source).digest('hex'));
        //     });
        // } catch(_e) {
        //     shelljs.rm('-rf', path.join(__dirname, `./temp/${baseName}.*`));
        //     resolve('');
        // }
    })
}


/**
 * 在C语言文件中添加setvbuf函数调用，以解决输出缓冲区溢出的问题。
 * 
 */
export async function add_servbuf(path) {

    return new Promise(async (resolve, reject) => {

        let data = await fp.readFile(path, 'utf8');

        // 使用正则表达式匹配是否存在#include <stdio.h>
        const includeStdioRegex = /#include\s+<stdio.h>/;
        const hasIncludeStdio = includeStdioRegex.test(data);

        // 如果没有引入stdio.h，则在首行插入#include <stdio.h>
        if (!hasIncludeStdio) {
            data = `#include <stdio.h>\n${data}`;
        }

        // 使用正则表达式匹配main函数的开始位置
        const mainMatch = data.match(/(?:\w+\s+)?main\s*\([^)]*\)\s*{/);
        if (!mainMatch) {
            reject('Main function not found')
            return;
        }

        // 获取匹配到的位置
        const mainIndex = mainMatch.index;

        // 在main函数的第一行之后插入代码
        const modifiedCode = `${data.substring(0, mainIndex + mainMatch[0].length)}\n    setvbuf(stdout, NULL, _IONBF, 0);\n${data.substring(mainIndex + mainMatch[0].length)}`;

        await fp.writeFile(path, modifiedCode, 'utf8');

        resolve('');
    });
}